package org.ztuffaha.amqsecurity;

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQQueueBrowser;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.ClassPathResource;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;
import java.util.List;
import java.util.ArrayList;
import java.util.ListIterator;
import java.io.File;
import java.io.FileWriter;

/**
 * @author ztuffaha
 * Date: Apr 22, 2008
 * Time: 2:37:38 PM
 * 
 * This class is a common helper class which is not part of the
 * functionalities within this project.
 * 
 * This class is hoped to act as a CLI for ActiveMQ and 
 * expanded later on.
 */
public class ActiveMqManager
{
	
    private static final Log LOG = LogFactory.getLog(ActiveMqManager.class);
    private String filepath;
    boolean throughJetty = false;

    public String getFilepath()
    {
        return this.filepath;
    }

    public void setFilepath(String filepath)
    {
        this.filepath = filepath;
    }

    AuthenticationMap mapTemp = new AuthenticationMap();
    private final String encryptionScheme = StringEncrypter.DESEDE_ENCRYPTION_SCHEME;

    public List browse(String queueName, String userName, String password)
    {
        List msgs = new ArrayList();
        try
        {
            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(userName, password, "tcp://localhost:61616");
            QueueConnection conn = connectionFactory.createQueueConnection(userName, password);
            QueueSession session = conn.createQueueSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
            Queue messageQueue = session.createQueue(queueName);
            conn.start();
            ActiveMQQueueBrowser browser = (ActiveMQQueueBrowser) session.createBrowser(messageQueue);
            java.util.Enumeration e = browser.getEnumeration();
            while (e.hasMoreElements())
            {
                javax.jms.TextMessage message = (javax.jms.TextMessage) e.nextElement();
                msgs.add(message);
            }
            browser.close();
            conn.close();
            return msgs;

        }
        catch (Exception e)
        {
            e.printStackTrace(); 
            return null;
        }
    }

    public void empty(String queueName, String userName, String password)
    {
        int cnt = 0;
        Hashtable env = new Hashtable();
        /*
         * zt: Hardcoded values need to be re-evaluated
         */
        env.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory");
        env.put(Context.PROVIDER_URL, "tcp://localhost:61616");
        Context jndiContext = null;
        try
        {
            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(userName, password, "tcp://localhost:61616");
            QueueConnection conn = connectionFactory.createQueueConnection(userName, password);
            QueueSession session = conn.createQueueSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
            Queue messageQueue = session.createQueue(queueName);
            QueueReceiver receiver = session.createReceiver(messageQueue);
            conn.start();
            while (true)
            {
                Message message = receiver.receive(200);
                if (message == null)
                {
                    break;
                }
            }
            conn.close();
        }
        catch (Exception e)
        {
            e.printStackTrace(); 
        }
    }

    public boolean changePassword(String userName, String oldPassword, String newPassword)
    {
        boolean status = false;
        try
        {
            XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("users.xml"));
            AuthList authList = (AuthList) beanFactory.getBean("AuthList");
            List myAuthList = authList.getEntries();
            for (int i = 0; i < myAuthList.size(); i++)
            {
                AuthEntry user = (AuthEntry) myAuthList.get(i);
                StringEncrypter encrypter = new StringEncrypter(encryptionScheme);
                if (userName.compareTo(user.getName()) == 0 && encrypter.decrypt(user.getPassword()).compareTo(oldPassword) == 0)
                {
                    user.setPassword(newPassword);
                    user.setEncrypted("false");
                    status = true;
                    mapTemp.addAuthEntry(user.getName(), user);
                    throughJetty = true;
                }
                else
                {
                    mapTemp.addAuthEntry(user.getName(), user);
                    status = false;
                }
            }
            List myQueueList = authList.getQueues();
            for (int i = 0; i < myQueueList.size(); i++)
            {
                QueueEntry queue = (QueueEntry) myQueueList.get(i);
                mapTemp.addQueueEntry(queue.getQueuename(), queue);
            }
            writeUsersXml();
            return status;
        }

        catch (Exception e)
        {
            e.printStackTrace(); 
            return false;
        }

    }

    public boolean addUser(String userName, String password)
    {

        boolean status = false;
        try
        {
            mapTemp = new AuthenticationMap();
            XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("users.xml"));
            AuthList authList = (AuthList) beanFactory.getBean("AuthList");
            List myAuthList = authList.getEntries();
            for (int i = 0; i < myAuthList.size(); i++)
            {
                AuthEntry user = (AuthEntry) myAuthList.get(i);
                StringEncrypter encrypter = new StringEncrypter(encryptionScheme);
                mapTemp.addAuthEntry(user.getName(), user);
            }
            AuthEntry user = new AuthEntry();
            user.setPassword(password);
            user.setName(userName);
            user.setEncrypted("false");
            mapTemp.addAuthEntry(user.getName(), user);
            throughJetty = true;
            status = true;

            List myQueueList = authList.getQueues();
            for (int i = 0; i < myQueueList.size(); i++)
            {
                QueueEntry queue = (QueueEntry) myQueueList.get(i);
                mapTemp.addQueueEntry(queue.getQueuename(), queue);
            }
            writeUsersXml();
            return status;
        }

        catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }

    }


    public void writeUsersXml()
    {
        StringBuffer buff = new StringBuffer();
        StringBuffer buffUsers = new StringBuffer();
        StringBuffer buffQueues = new StringBuffer();

        buff.setLength(0);
        buff.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?> " +
                "\n<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN//EN\" \"http://www.springframework.org/dtd/spring-beans.dtd\">");
        buff.append("\n<beans>\n\t<bean id=\"AuthList\" class=\"com.isone.activemq.AuthList\">");
        buff.append("\n\t\t<property name=\"entries\">\n\t\t\t<list>");

        List userList = mapTemp.getAllUsres();
        ListIterator userIterator = userList.listIterator();
        int i = 1;
        while (userIterator.hasNext())
        {
            String userName = (String) userIterator.next();
            buff.append("\n\t\t\t\t<ref bean=\"" + "User" + i + "\"/>");
            buffUsers.append("\n\t<bean id = \"User" + i + "\"" + " class=\"com.isone.activemq.AuthEntry\" >");
            buffUsers.append("\n\t\t<property name = \"name\" >");
            buffUsers.append("\n\t\t\t<value >" + userName + "</value >\n\t\t</property>");
            buffUsers.append("\n\t\t<property name = \"password\" >");
            AuthEntry auth = (AuthEntry) mapTemp.getAuthEntry(userName);
            try
            {
                StringEncrypter encrypter = new StringEncrypter(encryptionScheme);

                String encryptedPassword = encrypter.encrypt(auth.getPassword());
                if (throughJetty)
                {
                    auth.setEncrypted("false");
                    encryptedPassword = auth.getPassword();
                }
                else
                {
                    auth.setEncrypted("true");
                }

                buffUsers.append("\n\t\t\t<value >" + encryptedPassword + "</value >");
                buffUsers.append("\n\t\t</property>");
                buffUsers.append("\n\t\t<property name = \"encrypted\" >");
                buffUsers.append("\n\t\t\t<value >" + auth.getEncrypted() + "</value >");
                buffUsers.append("\n\t\t</property>");

                buffUsers.append("\n\t</bean>");
            }
            catch (StringEncrypter.EncryptionException e)
            {
                e.printStackTrace(); 
            }
            i++;
        }
        buff.append("\n\t\t\t</list >\n\t\t</property >");
        buff.append("\n\t\t<property name = \"queues\" >\n\t\t\t<list >");

        List queueList = mapTemp.getAllQueues();
        ListIterator queueListItr = queueList.listIterator();
        int q = 1;
        while (queueListItr.hasNext())
        {
            String queueName = (String) queueListItr.next();
            buff.append("\n\t\t\t\t<ref bean=\"" + "Queue" + q + "\"/>");
            buffQueues.append("\n\t<bean id = \"Queue" + q + "\"" + " class=\"com.isone.activemq.QueueEntry\" >");
            buffQueues.append("\n\t\t<property name = \"queuename\" >");
            buffQueues.append("\n\t\t\t<value >" + queueName + "</value >\n\t\t</property >");
            QueueEntry queue = (QueueEntry) mapTemp.getQueueEntry(queueName);
            buffQueues.append("\n\t\t<property name = \"admins\" >");
            buffQueues.append("\n\t\t\t<value >" + queue.getAdmins() + "</value >\n\t\t</property >");

            buffQueues.append("\n\t\t<property name = \"receivers\" >");
            buffQueues.append("\n\t\t\t<value >" + queue.getReceivers() + "</value >\n\t\t</property >");

            buffQueues.append("\n\t\t<property name = \"senders\" >");
            buffQueues.append("\n\t\t\t<value >" + queue.getSenders() + "</value >\n\t\t</property >");

            buffQueues.append("\n\t</bean>");

            q++;

        }
        buff.append("\n\t\t\t</list >\n\t\t</property >\n\t</bean>");
        StringBuffer finalBuff = new StringBuffer();
        finalBuff.append(buff.toString() + buffUsers.toString() + buffQueues.toString() + "\n</beans >");
        writeUsersXmlFileToDisk(finalBuff.toString());
    }

    public boolean writeUsersXmlFileToDisk(String contents)
    {
        try
        {
            ClassPathResource securityFileResource = new ClassPathResource("users.xml");
            File securityFile = securityFileResource.getFile();
            if (securityFile.exists())
            {
                securityFile.delete();
            }
            FileWriter resultsFile = new FileWriter(securityFile, true);
            resultsFile.write(contents);
            resultsFile.close();
            return true;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }
    }

    public boolean validateUser(String userName, String password)
    {
        boolean authenticated = false;
        try
        {
            XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("users.xml"));
            AuthList authList = (AuthList) beanFactory.getBean("AuthList");
            List myAuthList = authList.getEntries();
            for (int i = 0; i < myAuthList.size(); i++)
            {

                AuthEntry user = (AuthEntry) myAuthList.get(i);
                if (user.getName().compareTo(userName) == 0)
                {
                    StringEncrypter encrypter = new StringEncrypter(StringEncrypter.DESEDE_ENCRYPTION_SCHEME);
                    String decryptedPassword = encrypter.decrypt(user.getPassword());

                    if (decryptedPassword.compareTo(password) == 0)
                    {
                        authenticated = true;
                    }
                }
            }
            return authenticated;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return authenticated;

        }
    }


    /*
     * TODO: zt: this could be further refined.
     * 
     * This class could serve as a CLI to ActiveMQManager where we are able to browse, empty
     * and change the password
     * 
     */
    public static void main(String[] args)
    {
        String command = args[0];
        String queueName = args[1];
        ActiveMqManager mngr = new ActiveMqManager();
        if (command.compareTo("browse") == 0)
        {
            System.out.println("action: browse");
            //mngr.browse(queueName);
        }
        else if (command.compareTo("empty") == 0)
        {
            System.out.println("action: empty");
            //mngr.empty(queueName);
        }
        else if (command.compareTo("passwd") == 0)
        {
            System.out.println("action: change password");
            //mngr.empty(queueName);
        }
    }

}
